package com.jl.dbmapper;

import com.baomidou.mybatisplus.annotation.TableName;
import com.jl.LambdaUtils;
import com.jl.StringToolsUtils;
import lombok.Data;
import lombok.experimental.Accessors;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Update;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;

/**
 * 同步阻塞更新数值
 */
@Component
public class SyncUpdate {

    private static Util.Mapper mapper;

    @Autowired(required = false)
    private void setBaseMapper(SyncUpdate.Util.Mapper mapper) {
        SyncUpdate.mapper = mapper;
    }

    public Set update(Class<?> entity) {
        String tab = entity.getAnnotation(TableName.class).value();
        return new Set("update " + tab);
    }

    public static class Set {

        private String update;

        private String set;

        private String where;

        private Map<String, Object> map = new HashMap<>();

        public Set(String update) {
            this.update = update;
        }

        public <T> SetWhere set(LambdaUtils.JFunction<T, ?> jlFunction, BigDecimal value) {
            String property = StringToolsUtils.humpToLine(LambdaUtils.getProperty(jlFunction));
            String mapPropertyValue = Util.getMapProperty(map, property, value);
            this.set = String.format(" set %s = %s + %s", property, property, mapPropertyValue);
            this.where = String.format(" where %s + %s >= 0", property, mapPropertyValue);
            return new SetWhere(update, set, where, map);
        }
    }

    public static class SetWhere {

        private String update;

        private String set;

        private String where;

        private Map<String, Object> map;

        public SetWhere(String update, String set, String where, Map<String, Object> map) {
            this.update = update;
            this.set = set;
            this.where = where;
            this.map = map;
        }

        public <T> SetWhere set(LambdaUtils.JFunction<T, ?> jlFunction, BigDecimal value) {
            String property = StringToolsUtils.humpToLine(LambdaUtils.getProperty(jlFunction));
            String mapPropertyValue = Util.getMapProperty(map, property, value);
            this.set += String.format(", %s = %s + %s", property, property, mapPropertyValue);
            this.where += String.format(" and %s + %s >= 0", property, mapPropertyValue);
            return this;
        }

        public <T> SetWhere udateDateTime(LambdaUtils.JFunction<T, ?> jlFunction) {
            String property = StringToolsUtils.humpToLine(LambdaUtils.getProperty(jlFunction));
            String mapPropertyValue = Util.getMapProperty(map, property, LocalDateTime.now());
            this.set += String.format(", %s = %s", property, mapPropertyValue);
            return this;
        }

        public <T> Where eq(LambdaUtils.JFunction<T, ?> jlFunction, Object propertyValue) {
            String property = StringToolsUtils.humpToLine(LambdaUtils.getProperty(jlFunction));
            String mapPropertyValue = Util.getMapProperty(map, property, propertyValue);
            this.where += String.format(" and %s = %s", property, mapPropertyValue);
            return new Where(update, set, where, map);
        }
    }

    public static class Where {

        private String update;

        private String set;

        private String where;

        private Map<String, Object> map;

        public Where(String update, String set, String where, Map<String, Object> map) {
            this.update = update;
            this.set = set;
            this.where = where;
            this.map = map;
        }

        public <T, R> Where eq(LambdaUtils.JFunction<T, R> jlFunction, R value) {
            String property = StringToolsUtils.humpToLine(LambdaUtils.getProperty(jlFunction));
            String mapPropertyValue = Util.getMapProperty(map, property, value);
            this.where += String.format(" and %s = %s", property, mapPropertyValue);
            return this;
        }

        public SqlEntity getSql() {
            String sql = update + set + where;
            return new SqlEntity().setSql(sql).setParams(map);
        }

        public boolean exec() {
            int exec = mapper.exec(getSql().getSql(), map);
            return exec > 0 ? true : false;
        }

    }

    public static class Util {
        public interface Mapper {
            @Update("${sql}")
            int exec(@Param("sql") String sql, @Param("map") Map<String, Object> map);
        }

        public static String getMapProperty(Map<String, Object> map, String property, Object value) {
            String mapKey = property + "_value";
            map.put(mapKey, value);
            return String.format("#{map.%s}", mapKey);
        }
    }

    @Data
    @Accessors(chain = true)
    public static class SqlEntity {

        private String sql;

        private Map<String, Object> params;
    }
}
